package com.md.model.word;

import com.google.common.collect.Lists;
import com.md.model.base.Picture;
import com.md.model.base.StyleGroup;
import com.md.model.html.CustomLabel;
import com.md.model.html.LabelStyle;
import com.md.util.CollectionUtil;
import com.md.util.HtmlUtil;
import com.md.util.NodeUtil;
import com.md.util.ObjectUtil;
import com.md.util.POIUtil;
import com.md.util.StringUtil;
import com.md.value.html.ColorIndex;
import com.md.value.html.Font;
import com.md.value.html.LabelStyleType;
import com.md.value.html.Unit;
import lombok.Getter;
import org.apache.poi.xwpf.usermodel.BodyElementType;
import org.apache.poi.xwpf.usermodel.BodyType;
import org.apache.poi.xwpf.usermodel.IBody;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.IRunBody;
import org.apache.poi.xwpf.usermodel.XWPFHyperlink;
import org.apache.poi.xwpf.usermodel.XWPFHyperlinkRun;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlTokenSource;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTEmpty;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFldChar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHyperlink;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTMarkupRange;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTOnOff;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPTab;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRunTrackChange;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtContentRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSdtRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSmartTagRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTabs;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBrType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

import static com.md.util.HtmlUtil.createTabLabel;
import static com.md.util.POIUtil.getCTFldChar;
import static com.md.util.POIUtil.getFontStyle;
import static com.md.util.POIUtil.parseXmlObject2List;

/**
 * @author 刘骄阳
 * 2024-06-18 16:51
 */
@Getter
public class WordParagraph {
  private List<CustomLabel> labels;

  public WordParagraph(final IBodyElement element, final Integer index,
                       final Document documentStyle, final boolean useStyle) {
    XWPFParagraph paragraph = (XWPFParagraph) element;
    String styleID = paragraph.getStyleID();
    System.out.println("styleID: " + styleID);
    CTLvl number = getNumber(paragraph, documentStyle);
    System.out.println("number: " + number);
    labels = Lists.newArrayList();

    CustomLabel parent = new CustomLabel("div");
    // 定位
    Optional.ofNullable(paragraph.getCTP()).map(CTP::getPPr).ifPresent(pPr -> {
      POIUtil.getMarginLeft(pPr).ifPresent(parent::addStyle);
      POIUtil.getMarginRight(pPr).ifPresent(parent::addStyle);
      POIUtil.getMarginTop(pPr).ifPresent(parent::addStyle);
      POIUtil.getMarginBottom(pPr).ifPresent(parent::addStyle);
      POIUtil.getFirstLineIndent(pPr).ifPresent(parent::addStyle);
      POIUtil.getAlignment(pPr).ifPresent(parent::addStyle);
    });
    StyleGroup styleGroup = documentStyle.getCustomStyle(paragraph.getStyleID());
    List<String> styleNames = CollectionUtil.map(parent.getStyles(), LabelStyle::getStyleName);
    styleGroup.getStyles().stream().filter(labelStyle -> !styleNames.contains(labelStyle.getStyleName()))
      .forEach(parent::addStyle);
    List<XWPFRun> runs = paragraph.getRuns();

    List<CustomLabel> children = Lists.newArrayList();
    runs.forEach(xwpfRun -> {
      CustomLabel customLabel = readRUN(xwpfRun, paragraph, documentStyle, useStyle);
      // 行间距
//      xwpfRun.getTextPosition()
      children.add(customLabel);
    });
    parent.setChildrenList(children);
    labels.add(parent);
    if (runs.isEmpty()) {
      // 在下列情况下必须生成一个新行:
      // -有下一段/下一表
      // -如果正文是一个单元格(没有vMerge)并且只包含这一段
      if (isAddNewLine(paragraph, index)) {
        // 新增空行
        labels.add(new CustomLabel("br"));
      }
    } else {
      try {
//        labels.addAll(groupBySTFldCharType(paragraph.getCTP(), paragraph, documentStyle));
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }

    // Page Break
    // Cannot use paragraph.isPageBreak() because it throws NPE because
    // pageBreak.getVal() can be null.
    CTPPr ppr = paragraph.getCTP().getPPr();
    if (ppr != null) {
      if (ppr.isSetPageBreakBefore()) {
        CTOnOff pageBreak = ppr.getPageBreakBefore();
        if (pageBreak != null
          && (pageBreak.xgetVal() == null || POIUtil.isON(pageBreak.xgetVal()))) {
//          pageBreak();
        }
      }
    }
  }

  public static Optional<CTP> getCTP(final XWPFRun run) {
    return Optional.ofNullable(run).map(XWPFRun::getParent).map(iRunBody -> {
      if (iRunBody instanceof XWPFParagraph) {
        return (XWPFParagraph) iRunBody;
      }
      return null;
    }).map(XWPFParagraph::getCTP);
  }

  public static Optional<CTPPr> getCTPPr(final XWPFRun run) {
    return getCTP(run).map(CTP::getPPr);
  }

  public static Optional<CTTabs> getTabs(final XWPFRun run) {
    return getCTPPr(run).map(CTPPr::getTabs);
  }

  private boolean isAddNewLine(final XWPFParagraph paragraph, final int index) {
    IBody body = paragraph.getBody();
    List<IBodyElement> bodyElements = body.getBodyElements();
    // 如果是表格
    if (body.getPartType() == BodyType.TABLECELL && bodyElements.size() == 1) {
      XWPFTableCell cell = (XWPFTableCell) body;
      CTVMerge vMerge = cell.getCTTc().getTcPr().getVMerge();
      if (vMerge != null && ObjectUtil.equals(vMerge.xgetVal(), STMerge.CONTINUE)) {
        return false;
      }
      return Optional.of(cell).map(XWPFTableCell::getTableRow).map(XWPFTableRow::getTableCells)
        .flatMap(xwpfTableCells -> xwpfTableCells.stream().map(xwpfTableCell -> {
          if (xwpfTableCell.getBodyElements().size() != 1) {
            return false;
          }
          IBodyElement element = xwpfTableCell.getBodyElements().get(0);
          if (element.getElementType() != BodyElementType.PARAGRAPH) {
            return false;
          }
          return ((XWPFParagraph) element).getRuns().isEmpty();
        }).findFirst()).orElse(true);
    }
    return bodyElements.size() > index + 1;
  }

  public void getXmlObjectStyle(final XmlObject xmlObject, final XWPFParagraph paragraph, final Document documentStyle) {
    if (xmlObject instanceof CTBookmark) {
      // 书签

    } else if (xmlObject instanceof CTR) {
      CTR ctr = (CTR) xmlObject;

      CustomLabel labelByCTR = getLabelByCTR(ctr, paragraph, documentStyle);
    } else if (xmlObject instanceof CTMarkupRange) {
      // 书签、评论、修订

    }
  }

  public CustomLabel getLabelByCTR(final CTR ctr, final XWPFParagraph paragraph, final Document documentStyle) {
    StyleGroup styleGroup = documentStyle.getCustomStyle(paragraph.getStyleID());
    CTRPr rPr = ctr.getRPr();


    Font font = new Font(rPr, documentStyle.getThemeDocument().getThemeDocuments());
    font.getLabelStyle().forEach(styleGroup::addStyle);
    StringBuilder text = new StringBuilder();

    XmlCursor c = ctr.newCursor();
    c.selectPath("./*");


    return null;
  }

  public CustomLabel readRUN(final XWPFRun run, final XWPFParagraph paragraph,
                             final Document documentStyle, final boolean useStyle) {
    List<LabelStyle> styles = Lists.newArrayList();
    CTR ctr = run.getCTR();
    // 高亮
    if (run.isHighlighted()) {
      styles.add(LabelStyle.of(LabelStyleType.BACKGROUND_COLOR,
        ColorIndex.of(run.getTextHightlightColor().toString()).getHex()));
    }

    Optional.ofNullable(ctr.getRPr()).ifPresent(rPr -> {
      // 字体风格
      POIUtil.getFontFamily(rPr, documentStyle.getThemeDocument().getThemeDocuments()).ifPresent(styles::add);
      // 字体大小
      POIUtil.getFontSize(rPr).ifPresent(styles::add);
      // 加粗
      POIUtil.getBlob(rPr).ifPresent(styles::add);
      // 倾斜
      POIUtil.getItalic(rPr).ifPresent(styles::add);
      // 字体颜色
      POIUtil.getColor(rPr).ifPresent(styles::add);
      // 下划线、删除线
      POIUtil.getTextDecoration(rPr).ifPresent(styles::add);
      // 背景色
      POIUtil.getShd(rPr).ifPresent(styles::add);

      List<XmlObject> xmlObjectList = parseXmlObject2List(ctr.getRPr(), "./*");
      xmlObjectList.forEach(xmlObject -> {
        System.err.println(readXmlObject(xmlObject));
      });
    });
    // 文本
    CTText[] tArray = ctr.getTArray();

    StringBuilder content = new StringBuilder();
    if (ObjectUtil.notEmpty(tArray)) {
      for (final CTText ctText : tArray) {
        content.append(ctText.getStringValue());
      }
    }
    if (useStyle) {
      CustomLabel customLabel = new CustomLabel("span");
      customLabel.setContent(getTextBox(ctr, content.toString()));
      return customLabel;
    } else {
      return HtmlUtil.buildUseLabel("span", getTextBox(ctr, content.toString()), styles, null);
    }
  }

  public String readXmlObject(final XmlObject xmlObject) {
    Node node = xmlObject.getDomNode();
    String name = node.getNodeName();
    System.out.println(name);

    System.out.println("类型: " + name);
    if (xmlObject instanceof CTHyperlink) {
      CTHyperlink link = (CTHyperlink) xmlObject;
      String anchor = link.getAnchor();
      String href = null;
      // Test if the is an id for hyperlink
      String hyperlinkId = link.getId();
      if (ObjectUtil.notEmpty(hyperlinkId)) {
        XWPFHyperlink hyperlink = null;
//        XWPFHyperlink hyperlink = document.getHyperlinkByID(hyperlinkId);
        href = hyperlink != null ? hyperlink.getURL() : null;
      }
      for (CTR r : link.getRList()) {
        System.out.println();
//        XWPFRun run = new XWPFHyperlinkRun(link, r, paragraph);
//        visitRun(run, false, href != null ? href : (anchor != null) ? ("#" + anchor) : null);
      }
    } else if (xmlObject instanceof CTSdtRun) {
      CTSdtContentRun run = ((CTSdtRun) xmlObject).getSdtContent();
      for (CTR r : run.getRList()) {
        System.out.println();
//        XWPFRun ru = new XWPFRun(r, paragraph);
//        visitRun(ru, false, null);
      }
    } else if (xmlObject instanceof CTRunTrackChange) {
      for (CTR r : ((CTRunTrackChange) xmlObject).getRList()) {
        System.out.println();
//        XWPFRun run = new XWPFRun(r, paragraph);
//        List<XmlObject> xmlObjectList = parseXmlObject2List(run.getCTR(), "./*");
//        xmlObjectList.forEach(xmlObject -> {
//          try {
//            visitRun(paragraph, xmlObject, document);
//          } catch (Exception e) {
//            throw new RuntimeException(e);
//          }
//        });

      }
    } else if (xmlObject instanceof CTSimpleField) {
      CTSimpleField simpleField = (CTSimpleField) xmlObject;
      String instr = simpleField.getInstr();
      System.out.println(instr);
      for (CTR r : simpleField.getRList()) {
        System.out.println();
//        XWPFRun run = new XWPFRun(r, paragraph);
//        List<XmlObject> xmlObjectList = parseXmlObject2List(run.getCTR(), "./*");
//        xmlObjectList.forEach(xmlObject -> {
//          try {
//            visitRun(paragraph, xmlObject, document);
//          } catch (Exception e) {
//            throw new RuntimeException(e);
//          }
//        });
//        visitRun(paragraph, run.getCTR(), document);
      }
    } else if (xmlObject instanceof CTSmartTagRun) {
      System.out.println();
      // Smart Tags can be nested many times.
      // This implementation does not preserve the tagging information
//      labels.addAll(groupBySTFldCharType(o, paragraph, document));
    } else if (xmlObject instanceof CTBookmark) {
      System.out.println();
      CTBookmark bookmark = (CTBookmark) xmlObject;
//      visitBookmark(bookmark, paragraph, paragraphContainer);
    } else if (xmlObject instanceof CTMarkupRange) {
      System.out.println();
      System.out.println("这是批注: " + xmlObject);
    } else {
      if (name.equals("w14:reflection")) {
        StringBuilder htmlBuilder = new StringBuilder();
        htmlBuilder.append("<div style=\"");
        // <w14:reflection w14:blurRad="6350" w14:stA="55000" w14:stPos="0" w14:endA="50" w14:endPos="85000" w14:dist="60007" w14:dir="5400000" w14:fadeDir="5400000" w14:sx="100000" w14:sy="-100000" w14:kx="0" w14:algn="bl"/>
        NamedNodeMap attributes = node.getAttributes();


        // sx; sy;
        BigDecimal zero = new BigDecimal(0);
        BigDecimal sx = Optional.ofNullable(attributes.getNamedItem("w14:sx")).map(Node::getNodeValue)
          .map(Double::parseDouble).map(Unit::convertEmuToPx).orElse(zero);
        BigDecimal sy = Optional.ofNullable(attributes.getNamedItem("w14:sy")).map(Node::getNodeValue)
          .map(Double::parseDouble).map(Unit::convertEmuToPx).orElse(zero);
        if (!sx.equals(zero) && !sy.equals(zero)) {
          // 反射缩放
          htmlBuilder.append("transform: scale(").append(sx).append(", ").append(sy).append(");");
        }
        htmlBuilder.append("position: absolute;");

        int length = attributes.getLength();
        for (int i = 0; i < length; i++) {
          Node item = attributes.item(i);
          String nodeName = item.getNodeName();
          String nodeValue = item.getNodeValue();
          switch (nodeName) {
            case "w14:blurRad":
              // 模糊半径，单位为像素，控制反射的模糊程度。
              htmlBuilder.append("filter: blur(").append(Unit.convertEmuToPx(Double.parseDouble(nodeValue))).append("px); ");
              break;
            case "w14:stA":
              // 起始透明度，通常在 0 到 100 之间，表示反射的透明度
              htmlBuilder.append("opacity: ").append(Double.parseDouble(nodeValue) / 100000.0).append("; "); // 起始透明度
              break;
            case "w14:stPos":
              // 起始位置，单位为百分比，表示反射起始的相对位置。
              break;
            case "w14:endPos":
              // 结束位置，单位为百分比，表示反射结束的相对位置。
              break;
            case "w14:dist":
              // 距离，单位为像素，控制反射与原对象之间的距离。
              htmlBuilder.append("transform: translateY(").append(Unit.convertEmuToPx(Double.parseDouble(nodeValue))).append("px) "); // 位置
              break;
            case "w14:dir":
              // 方向，通常以度数表示，影响反射的方向。
              htmlBuilder.append("rotate(").append(Double.parseDouble(nodeValue) / 60000.0).append("deg); "); // 方向
              break;
            case "w14:fadeDir":
              // 淡化方向，通常也以度数表示。
              htmlBuilder.append("direction: ").append(Double.parseDouble(nodeValue)).append("deg;"); // 渐变方向
              break;
            case "w14:kx":
              // 水平倾斜，单位为百分比，控制反射的倾斜效果。
              break;
            case "w14:algn":
              // 对齐方式，通常为文本的对齐方式，如左下角等。
              htmlBuilder.append("text-align: ").append(nodeValue.equals("bl") ? "left" : "center").append("; ");
              break;
            default:
              break;
          }
        }
        // 创建 HTML 反射效果的表示
        htmlBuilder.append("\">");
        htmlBuilder.append("反射文本"); // 可以替换为实际文本
        htmlBuilder.append("</div>");
        return htmlBuilder.toString();
      }
    }
    return "";
  }

  public String getTextBox(final CTR ctr, final String text) {
    if (ObjectUtil.notEmpty(text)) {
      return text;
    }
    return NodeUtil.findNode(ctr.getDomNode(), "txbxContent").map(NodeUtil::readNodeValue).orElse("");
  }

  public void getFldChars(final CTR ctr) {
    Optional<CTFldChar> ctFldChar = Optional.ofNullable(ctr.getFldCharArray()).map(POIUtil::getFirst).orElse(null);
    // 判断域
    if (ctFldChar.isPresent()) {
      System.out.println();

    } else {
      // 处理域

      System.out.println();
    }
  }

  private CTLvl getNumber(final XWPFParagraph paragraph, final Document documentStyle) {
    return Optional.ofNullable(paragraph).map(XWPFParagraph::getCTP).map(CTP::getPPr).map(CTPPr::getNumPr)
      .flatMap(originalNumPr -> POIUtil.getNumber(originalNumPr, documentStyle).flatMap(ctNumPr ->
        POIUtil.getXWPFNum(ctNumPr, documentStyle).flatMap(xwpfNum ->
          POIUtil.getXWPFAbstractNum(xwpfNum, documentStyle).map(abstractNum -> {
            int level = 0;
            if (ctNumPr.getIlvl() != null) {
              level = ctNumPr.getIlvl().getVal().intValue();
            }
            return abstractNum.getAbstractNum().getLvlArray(level);
          })))).orElse(null);
  }

  private List<XmlObject> getStartToEnd(final List<XmlObject> xmlObjectList, final Integer startIndex,
                                        final Predicate<XmlObject> isTrue) {

    List<XmlObject> between = Lists.newArrayList();
    int length = xmlObjectList.size();
    for (int i = startIndex + 1; i < length; i++) {
      XmlObject xmlObject = xmlObjectList.get(i);
      if (isTrue.test(xmlObject)) {
        between.add(xmlObject);
      } else {
        break;
      }
    }
    return between;

  }

  private List<CustomLabel> groupBySTFldCharType(final XmlTokenSource tokenSource, final XWPFParagraph paragraph,
                                                 final Document document)
    throws Exception {

    List<XmlObject> xmlObjectList = parseXmlObject2List(tokenSource, "child::*");

    Integer continueCount = 0;
    List<CustomLabel> customLabels = Lists.newArrayList();
    for (int i = 0; i < xmlObjectList.size(); i++) {
      if (continueCount != 0) {
        continueCount--;
        continue;
      }
      XmlObject xmlObject = xmlObjectList.get(i);
      if (xmlObject instanceof CTR) {
        // Word文档中的字段是根据某些条件自动生成的数据片段，例如当前日期、页码或计算得出的值。
        // 字段常用于动态内容的生成，比如自动更新的目录、页眉页脚、日期时间等。
        // CTFldChar 主要用来标识字段的起始和结束位置，帮助解析和处理Word文档中的这些特殊数据区域。
        CTR ctr = (CTR) xmlObject;
        Optional<STFldCharType.Enum> ctFldChar = getCTFldChar(ctr).map(CTFldChar::getFldCharType);
        if (ctFldChar.isPresent()) {
          STFldCharType.Enum fldCharType = ctFldChar.get();
          if (fldCharType.equals(STFldCharType.BEGIN)) {
            // 检测到开始的时候, 拿取开始到结束的XmlObject
            List<XmlObject> startToEnd = getStartToEnd(xmlObjectList, i, e -> {
              if (e instanceof CTR) {
                Optional<STFldCharType.Enum> sTFldCharType =
                  getCTFldChar((CTR) xmlObject).map(CTFldChar::getFldCharType);
                return sTFldCharType.map(STFldCharType.BEGIN::equals).orElse(true);
              } else {
                return true;
              }
            });
            continueCount = startToEnd.size();
            customLabels.addAll(process(document, startToEnd));
          }
        } else {
          customLabels.add(readRun(new XWPFRun(ctr, (IRunBody) paragraph), document));
        }
      } else {
        visitRun(paragraph, xmlObject, document);
      }
    }
    return customLabels;
  }

  private List<CustomLabel> process(final Document document,
                                    final List<XmlObject> rListAfterSeparate) {
    List<CustomLabel> customLabels = Lists.newArrayList();
    if (rListAfterSeparate != null) {
      for (XmlObject xmlObject : rListAfterSeparate) {
        if (xmlObject instanceof CTR) {
          customLabels.add(readRun(new XWPFRun((CTR) xmlObject, (IRunBody) document.getXwpfDocument()), document));
        }
      }
    }
    return customLabels;
  }

  private void visitRun(final XWPFParagraph paragraph, final XmlObject o, final Document document)
    throws Exception {
    System.out.println("类型: " + o.schemaType().getName());
    if (o instanceof CTHyperlink) {
      CTHyperlink link = (CTHyperlink) o;
      String anchor = link.getAnchor();
      String href = null;
      // Test if the is an id for hyperlink
      String hyperlinkId = link.getId();
      if (ObjectUtil.notEmpty(hyperlinkId)) {
        XWPFHyperlink hyperlink = null;
//        XWPFHyperlink hyperlink = document.getHyperlinkByID(hyperlinkId);
        href = hyperlink != null ? hyperlink.getURL() : null;
      }
      for (CTR r : link.getRList()) {
        XWPFRun run = new XWPFHyperlinkRun(link, r, paragraph);
//        visitRun(run, false, href != null ? href : (anchor != null) ? ("#" + anchor) : null);
      }
    } else if (o instanceof CTSdtRun) {
      CTSdtContentRun run = ((CTSdtRun) o).getSdtContent();
      for (CTR r : run.getRList()) {
        XWPFRun ru = new XWPFRun(r, paragraph);
//        visitRun(ru, false, null);
      }
    } else if (o instanceof CTRunTrackChange) {
      for (CTR r : ((CTRunTrackChange) o).getRList()) {
        XWPFRun run = new XWPFRun(r, paragraph);
        List<XmlObject> xmlObjectList = parseXmlObject2List(run.getCTR(), "./*");
        xmlObjectList.forEach(xmlObject -> {
          try {
            visitRun(paragraph, xmlObject, document);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
        });

      }
    } else if (o instanceof CTSimpleField) {
      CTSimpleField simpleField = (CTSimpleField) o;
      String instr = simpleField.getInstr();
      System.out.println(instr);
      for (CTR r : simpleField.getRList()) {
        XWPFRun run = new XWPFRun(r, paragraph);
        List<XmlObject> xmlObjectList = parseXmlObject2List(run.getCTR(), "./*");
        xmlObjectList.forEach(xmlObject -> {
          try {
            visitRun(paragraph, xmlObject, document);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
        });
        visitRun(paragraph, run.getCTR(), document);
      }
    } else if (o instanceof CTSmartTagRun) {
      // Smart Tags can be nested many times.
      // This implementation does not preserve the tagging information
      labels.addAll(groupBySTFldCharType(o, paragraph, document));
    } else if (o instanceof CTBookmark) {
      CTBookmark bookmark = (CTBookmark) o;
//      visitBookmark(bookmark, paragraph, paragraphContainer);
    } else if (o instanceof CTMarkupRange) {
      System.out.println("这是批注: " + o);
    }
  }

  protected CustomLabel readRun(final XWPFRun run, final Document documentStyle) {
    CustomLabel customLabel = new CustomLabel("span");
    CTR ctr = run.getCTR();
    CTRPr rPr = ctr.getRPr();
    getFontStyle(rPr, documentStyle.getThemeDocument().getThemeDocuments()).forEach(customLabel::addStyle);
    List<XmlObject> xmlObjectList = parseXmlObject2List(ctr, "./*");
    List<CustomLabel> childrenList = Lists.newArrayList();
    xmlObjectList.forEach(xmlObject -> {
      if (xmlObject instanceof CTText) {
        CTText ctText = (CTText) xmlObject;
        String tagName = xmlObject.getDomNode().getNodeName();
        // 指令代码, 不知道啥用, 先打印出来看看
        if ("w:instrText".equals(tagName)) {
          String instrText = xmlObject.getDomNode().getNodeValue();
          System.err.println(instrText);
          System.err.println(ctText);
          System.err.println(ctText.getStringValue());
        } else {
          CustomLabel children = new CustomLabel("span");
          children.setParent(customLabel);
          children.setContent(ctText.getStringValue());
          childrenList.add(children);
        }
      } else if (xmlObject instanceof CTPTab) {
        System.err.println("未知模块: tab" + StringUtil.getToString(xmlObject));
      } else if (xmlObject instanceof CTBr) {
        // 需要换行
        CTBr ctBr = (CTBr) xmlObject;
        STBrType.Enum type = ctBr.getType();
        System.out.println(type);
        childrenList.add(new CustomLabel("br"));
      } else if (xmlObject instanceof CTEmpty) {
        String tagName = xmlObject.getDomNode().getNodeName();
        if ("w:tab".equals(tagName)) {
          Optional<CTTabs> tabs = getTabs(run);
          if (tabs.isPresent()) {
            childrenList.add(readTables(tabs.get()));
          } else {
            childrenList.add(createTabLabel("span"));
          }
        }
        if ("w:br".equals(tagName)) {
          // 需要换行
          childrenList.add(new CustomLabel("br"));
        }
        if ("w:cr".equals(tagName)) {
          childrenList.add(new CustomLabel("br"));
        }
      } else if (xmlObject instanceof CTDrawing) {
        childrenList.addAll(readDrawing((CTDrawing) xmlObject, documentStyle));
      }
    });
    customLabel.setChildrenList(childrenList);
    return customLabel;
  }

  public List<Picture> readDrawing(final CTDrawing drawing, final Document document) {
    List<Picture> pictures = Lists.newArrayList();
    List<CTInline> inlines = drawing.getInlineList();
    for (CTInline inline : inlines) {
      readGraphical(document, inline.getGraphic(), pictures);
    }
    List<CTAnchor> anchors = drawing.getAnchorList();
    for (CTAnchor anchor : anchors) {
      readGraphical(document, anchor.getGraphic(), pictures);
    }
    return pictures;
  }

  private void readGraphical(final Document document, final CTGraphicalObject graphic,
                             final List<Picture> pictures) {
    Optional.ofNullable(graphic).map(CTGraphicalObject::getGraphicData).ifPresent(graphicData -> {
      List<XmlObject> xmlObjectList = parseXmlObject2List(graphicData, "./*");
      for (XmlObject xmlObject : xmlObjectList) {
        if (xmlObject instanceof CTPicture) {
          CTPicture picture = (CTPicture) xmlObject;
          pictures.add(new Picture(picture, document));
        }
      }
    });
  }

  public CustomLabel readTables(final CTTabs tables) {
    System.out.println("tables: " + StringUtil.objectToJson(tables));
    return createTabLabel("span");
  }
}
